package com.sikaryofficial.backend.utils;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.crypto.digest.DigestUtil;
import com.alibaba.excel.annotation.ExcelProperty;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.sikaryofficial.backend.config.MinioConfig;
import com.sikaryofficial.backend.domain.vo.export.ProductExportVO;
import com.sikaryofficial.backend.domain.vo.export.ProductImportVO;
import com.sikaryofficial.common.core.domain.R;
import com.sikaryofficial.common.core.exception.ServiceException;
import com.sikaryofficial.common.core.utils.SpringUtils;
import com.sikaryofficial.system.api.RemoteFileService;
import com.sikaryofficial.system.api.domain.SysFile;
import com.sikaryofficial.system.api.model.att.AttachmentResp;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.PictureData;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.text.MessageFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import static com.sikaryofficial.common.core.utils.file.FileUtils.fileToMultipartFile;

/**
 * @author : qinjinyuan
 * @desc : 产品工具类
 * @date : 2023/11/30 11:03
 */
@Slf4j
public class ProductUtil {

    public static final String PARAM_SPLIT_KEY = "-";
    private static final String SPLIT_KEY = "_";
    private static final String METHOD_PATTERN = "getUrl";

    /**
     * 长整型转16进制
     *
     * @param id
     * @return
     */
    public static String toHexString(Long id) {
        return Long.toHexString(id);
    }

    public static Long getIdByHexStr(String hexValue) {
        // 16进制，radix 参数请勿调整
        return Long.parseUnsignedLong(hexValue, 16);
    }

    /**
     * 构建产品md5
     * <p>
     * select t.brand_taste_id ,md5(CONCAT(t.brand_no,'_',t.model,'_',t.style,'_',t.taste_name) ) from product_t t
     *
     * @param brandNo   品牌编号
     * @param model     型号
     * @param style     风格
     * @param tasteName 口味
     * @return
     */
    public static String buildProductMd5(String brandNo, String model, String style, String tasteName) {
        if (CharSequenceUtil.isBlank(brandNo) || CharSequenceUtil.isBlank(model) || CharSequenceUtil.isBlank(style) || CharSequenceUtil.isBlank(tasteName)) {
            throw new ServiceException("brandNo or model or style or tasteName is empty.");
        }
        return DigestUtil.md5Hex(brandNo.concat(SPLIT_KEY).concat(model)
                .concat(SPLIT_KEY).concat(style)
                .concat(SPLIT_KEY).concat(tasteName));
    }

    /**
     * 根据Excel注解获取字段
     *
     * @param columnName 注解名称
     * @param fields     字段数组
     * @return
     */
    public static Field getFieldByExcelProperty(String columnName, Field[] fields) {
        return Stream.of(fields)
                .filter(field -> Objects.nonNull(field.getAnnotation(ExcelProperty.class))
                        && Objects.nonNull(field.getAnnotation(ExcelProperty.class).value())
                        && Arrays.asList(field.getAnnotation(ExcelProperty.class).value()).contains(columnName))
                .findFirst().orElse(null);
    }

    /**
     * 通过反射设置字段的值
     *
     * @param productImportVO
     * @param columnIndex     字段索引
     * @param fieldValue      字段值
     */
    public static void setFieldValByReflect(ProductImportVO productImportVO, int columnIndex, Object fieldValue) {
        if (Objects.isNull(fieldValue) || CharSequenceUtil.isBlank(fieldValue.toString())) {
            return;
        }
        // 根据ProductImportVO类的注解ExcelProperty的属性index值进行 反射设置
        // hutool 通过反射工具，获取ProductImportVO 类的所有get方法
        Field[] fields = ReflectUtil.getFields(ProductImportVO.class);
        // 字段上有多个注解
        Field targetField = Stream.of(fields).filter(field -> Objects.nonNull(field.getAnnotation(ExcelProperty.class)) && columnIndex == field.getAnnotation(ExcelProperty.class).index()).findFirst().orElse(null);
        if (Objects.isNull(targetField)) {
            return;
        }
        // 获取字段的get方法
        Method[] methods = ReflectUtil.getMethods(ProductImportVO.class, method -> CharSequenceUtil.equalsIgnoreCase(method.getName(), "set" + targetField.getName()));
        if (Objects.nonNull(methods) && methods.length > 0) {
            try {
                ReflectUtil.invoke(productImportVO, methods[0], fieldValue);
            } catch (Exception e) {
                log.error("setPicUrlByReflect failed: {},{}", methods, fieldValue);
                log.error("setPicUrlByReflect failed: {}", e);
            }
        }
    }

    /**
     * 设置图片url字段的值
     *
     * @param picMap
     * @param productImportVO
     * @param columnIndex
     * @param key
     */
    public static void setPicUrl(Map<String, PictureData> picMap, ProductImportVO productImportVO, int columnIndex, String key) {
        PictureData pictureData = picMap.get(key);
        log.info("getPictureType={}", pictureData.getPictureType());
        log.info("getMimeType={}", pictureData.getMimeType());
        log.info("suggestFileExtension={}", pictureData.suggestFileExtension());

        // 上传二进制数据，并返回URL地址
        RemoteFileService remoteFileService = SpringUtils.getBean(RemoteFileService.class);
        R<SysFile> sysFileR = remoteFileService.upload(
                fileToMultipartFile(byte2File(pictureData.getData(), "/data/tmp", IdWorker.getIdStr().concat(".png"))));
        String url = "";
        if (Objects.nonNull(sysFileR) && Objects.nonNull(sysFileR.getData())) {
            url = sysFileR.getData().getUrl();
            log.info("url={}", url);
        }
        // 反射设置--图片数据要进行前缀处理
        setFieldValByReflect(productImportVO, columnIndex, url);
    }

    /**
     * byte数组转文件
     *
     * @param buf
     * @param filePath
     * @param fileName
     * @return
     */
    public static File byte2File(byte[] buf, String filePath, String fileName) {
        File file = new File(filePath + File.separator + fileName);
        try (
                FileOutputStream fos = new FileOutputStream(file);
                BufferedOutputStream bos = new BufferedOutputStream(fos)
        ) {
            File dir = new File(filePath);
            dir.mkdirs();
            bos.write(buf);
            return file;
        } catch (Exception e) {
            log.error("byte2File failed: ", e);
        }
        return null;
    }

    /**
     * 附件数据
     *
     * @param productImportVO
     * @param attachmentList
     */
    public static void buildAttachment(ProductImportVO productImportVO, List<AttachmentResp> attachmentList) {
        // hutool 通过反射工具，获取ProductImportVO 类的所有get方法
        Method[] methods = ReflectUtil.getMethods(ProductImportVO.class, method -> method.getName().startsWith(METHOD_PATTERN));
        // 按照method.getName()进行排序: getUrl1, getUrl2, getUrl3, getUrl4, getUrl5, getUrl6, getUrl7, getUrl8, getUrl9, getUrl10, getUrl11, getUrl12
        // 自定义Comparator来解析方法名并按数字顺序排序
        Comparator<Method> methodComparator = (m1, m2) -> {
            // 提取方法名中的数字部分
            int num1 = extractNumber(m1.getName());
            int num2 = extractNumber(m2.getName());
            // 否则按照数字部分排序
            return Integer.compare(num1, num2);
        };

        List<Method> methodList = Arrays.stream(methods).sorted(methodComparator).collect(Collectors.toList());
        // 遍历 methods
        for (Method method : methodList) {
            Object obj = ReflectUtil.invoke(productImportVO, method);
            if (Objects.isNull(obj) || CharSequenceUtil.isBlank(obj.toString())) {
                continue;
            }
            attachmentList.add(AttachmentResp.builder().attachmentId(IdWorker.getId()).url(obj.toString()).build());
        }
    }

    private static int extractNumber(String methodName) {
        // 简单示例，假设数字总是在"getUrl"之后且没有其他非数字字符
        return Integer.parseInt(methodName.replace(METHOD_PATTERN, ""));
    }

    /**
     * 构建附件数据
     *
     * @param attacheMap
     * @param vo
     */
    public static void buildAttachmentData(Map<Long, List<AttachmentResp>> attacheMap, ProductExportVO vo) {
        List<AttachmentResp> attachmentRespList = attacheMap.get(vo.getBrandTasteId());
        if (CollUtil.isEmpty(attachmentRespList)) {
            return;
        }
        // hutool 通过反射工具，获取ProductImportVO 类的所有get方法
        Method[] methods = ReflectUtil.getMethods(ProductExportVO.class, method -> method.getName().startsWith("setUrl"));
        for (int i = 0; i < attachmentRespList.size(); i++) {
            AttachmentResp attachmentResp = attachmentRespList.get(i);
            String setUrlName = "setUrl" + (i + 1);
            Stream.of(methods).filter(method -> method.getName().equals(setUrlName))
                    .findFirst().ifPresent(
                            method -> {
                                try {
                                    ReflectUtil.invoke(vo, method, MinioConfig.getUrlPrefix(attachmentResp.getUrl()));
                                } catch (Exception e) {
                                    log.warn("productImport=error");
                                }
                            }
                    );
        }
    }

    /**
     * 把文件打包并输出到输出流 response.getOutputStream()
     * <p>
     *
     * @param folder
     * @return 返回 zip文件绝对路径
     */
    public static String genLocalZipFile(File folder, Long objectId, Long taskId) {
        String outputZipPath = "/data/tmp/qrcode/output/{0}/{1}/{2}/";
        outputZipPath = MessageFormat.format(outputZipPath, objectId.toString(), taskId.toString(),
                LocalDateTimeUtil.format(LocalDateTime.now(), DateTimeFormatter.ofPattern(DatePattern.PURE_DATE_PATTERN)));
        File dir = new File(outputZipPath);
        dir.mkdirs();
        String outputZipFile = outputZipPath.concat(IdWorker.getIdStr().concat(".zip"));

        try (ZipOutputStream out = new ZipOutputStream(Files.newOutputStream(new File(outputZipFile).toPath()))) {
            // 获取图片文件夹下的所有文件
            File[] files = folder.listFiles();
            int count = 0;
            assert files != null;
            for (File file : files) {
                if (file.isFile()) {
                    // 添加文件到压缩文件
                    out.putNextEntry(new ZipEntry(file.getName()));
                    FileInputStream in = new FileInputStream(file);
                    byte[] buffer = new byte[1024];
                    int length;
                    while ((length = in.read(buffer)) > 0) {
                        out.write(buffer, 0, length);
                    }
                    in.close();
                    out.closeEntry();
                    count++;
                }
            }
            log.info("downloadQrCode success, count:{}", count);
        } catch (Exception e) {
            log.error("productExport:{}", e);
            throw new ServiceException("downloadQrCode error.");
        } finally {
            // 删除图片文件夹下的所有文件
            FileUtil.del(folder);
        }
        return outputZipFile;
    }
}
